package com.heima.component;

import com.heima.entity.constants.Constants;
import com.heima.entity.dto.DownloadFileDto;
import com.heima.entity.dto.SysSettingsDto;
import com.heima.entity.dto.UserSpaceDto;
import com.heima.entity.po.FileTreeNode;
import com.heima.entity.po.UserInfo;
import com.heima.entity.query.UserInfoQuery;
import com.heima.exception.BusinessException;
import com.heima.mapper.FileInfoMapper;
import com.heima.mapper.UserInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

@Component
@Slf4j
public class RedisComponent {
    @Resource
    private RedisUtils redisUtils;

    @Resource
    private FileInfoMapper fileInfoMapper;
    @Resource
    private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;
    @Resource
    private RedisTemplate redisTemplate;
    public SysSettingsDto getsysSettingsDto(){
        SysSettingsDto sysSettingsDto = (SysSettingsDto)redisUtils.get(Constants.REDIS_KEY_SYS_SETTING);
        if(sysSettingsDto == null){
            sysSettingsDto = new SysSettingsDto();
            redisUtils.set(Constants.REDIS_KEY_SYS_SETTING,sysSettingsDto);
//            log.info("redis创建完成");
        }
        return sysSettingsDto;
    }

    public void saveUserSpace(String userId, UserSpaceDto userSpaceDto){
        redisUtils.setex(Constants.REDIS_KEY_USER_SPACE_USE+userId,userSpaceDto,Constants.REDIS_KEY_EXPIRES_DAY);
    }
    public UserSpaceDto getUserSpaceUse(String userId) {
        UserSpaceDto spaceDto = (UserSpaceDto) redisUtils.get(Constants.REDIS_KEY_USER_SPACE_USE + userId);
        if (null == spaceDto) {
            spaceDto = new UserSpaceDto();
            Long useSpace = this.fileInfoMapper.selectUseSpace(userId);
            spaceDto.setUseSpace(useSpace);
            spaceDto.setTotalSpace(getsysSettingsDto().getUserInitUseSpace() * Constants.MB);
            redisUtils.setex(Constants.REDIS_KEY_USER_SPACE_USE + userId, spaceDto, Constants.REDIS_KEY_EXPIRES_DAY);
        }
        return spaceDto;
    }

    public void saveFileTempSize(String userId,String fillId,Long fileSize){
        Long currentSize = getFileTempSize(userId,fillId);
        redisUtils.setex(Constants.REDIS_KEY_USER_FILE_TEMP_SIZE+userId+fillId,currentSize+fileSize,Constants.REDIS_KEY_EXPIRES_ONE_HOUR);
    }

    //获取临时文件大小
    public Long getFileTempSize(String userId,String fileId){
        Long currentSize = getFileSizeFromRedis(Constants.REDIS_KEY_USER_FILE_TEMP_SIZE+userId+fileId);
        return currentSize;
    }

    public Long getFileSizeFromRedis(String key){
        Object o = redisUtils.get(key);
        if (o == null){
            return 0L;
        }
        if (o instanceof Integer){
            return ((Integer)o).longValue();
        } else if (o instanceof Long) {
            return (Long)o;

        }
        return 0L;
    }

    public void saveDownloadCode(String code, DownloadFileDto downloadFileDto){
        redisUtils.setex(Constants.REDIS_KEY_DOWNLOAD + code,downloadFileDto,Constants.REDIS_KEY_EXPIRES_FIVE_MIN);
    }

    public DownloadFileDto getDownloadCode(String code) {
       return (DownloadFileDto) redisUtils.get(Constants.REDIS_KEY_DOWNLOAD + code);
    }

    public void saveSysSettingsDto(SysSettingsDto sysSettingsDto) {
        redisUtils.set(Constants.REDIS_KEY_SYS_SETTING,sysSettingsDto);
    }

    public void saveFileInfo2Cache(String userId,FileTreeNode fileTreeNode){
        redisUtils.sethash(Constants.REDIS_HASH_KEY_FILE_TREE+userId,fileTreeNode);
    }
    public FileTreeNode getFileInfo2Cache(String userId,String fileId){
        return redisUtils.gethash(userId, fileId);
    }

    public void saveUserInfo(UserInfo userInfo){
        //解决缓存雪崩问题
        Random random = new Random();
        int i = random.nextInt(11);
        Long time = (long) i + 1440L;
        redisUtils.setex(Constants.KEY_USER_INFO+userInfo.getUserId(),userInfo,time);
    }


    //基于分布式锁解决缓存击穿问题
    public UserInfo getUserInfo(String userId) throws InterruptedException {
        UserInfo o = (UserInfo)redisUtils.get(Constants.KEY_USER_INFO + userId);

        //用分布式锁控制线程
        if (o == null){
            String lockKey = Constants.LOCK_KEY_USER + userId;
            //获得分布式锁
            boolean b = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, userId, 30, TimeUnit.SECONDS));
            if (b){
                try{
                    UserInfo userInfo = userInfoMapper.selectByUserId(userId);
                    if (userInfo == null){
                        redisUtils.setex(Constants.KEY_USER_INFO + userId,"NULL",30L);
                        throw new BusinessException("信息不存在，请重试或联系管理员");
                    }else {
                        saveUserInfo(userInfo);
                    }
                }finally {
                    //释放分布式锁
                    if (userId.equals(redisUtils.get(lockKey))){
                        redisTemplate.delete(lockKey);
                    }
                }
            }else {
//                retryGetLock2UserInfo(lockKey,userId);
                Thread.sleep(50);
                getUserInfo(userId);
            }
        }
        return o;
    }
    //重试机制
    private void retryGetLock2UserInfo(String lockKey,String userId){
        int maxRetries = 3;
        int retryCount = 0;
        while (retryCount < maxRetries){
            try {
                Thread.sleep(50);
                boolean b = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, userId, 30, TimeUnit.SECONDS));
                if (b){
                    try{
                        UserInfo userInfo = userInfoMapper.selectByUserId(userId);
                        if (userInfo == null){
                            redisUtils.setex(Constants.KEY_USER_INFO + userId,"NULL",30L);
                            throw new BusinessException("信息不存在，请重试或联系管理员");
                        }else {
                            saveUserInfo(userInfo);
                        }
                    }finally {
                        //释放分布式锁
                        if (userId.equals(redisUtils.get(lockKey))){
                            redisTemplate.delete(lockKey);
                        }
                    }
                    break;
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException(e);
            }finally {
                retryCount++;
            }
        }
    }

    //缓存用户基本信息和根文件夹信息解决缓存穿透用缓存null值，不用布隆过滤器。
    //而缓存分享的文件和数据详情可以用布隆过滤器。

    public static void main(String[] args) {
//        int[] nums = new int[]{-1,0,1,2,-1,-4};
        int[] nums = new int[]{0,0,0};
//        IntStream sorted = Arrays.stream(nums).sorted();
        int[] array = Arrays.stream(nums).sorted().toArray();
        int head = 0;
        int last = array.length-1;
        List<Integer[]> list = new ArrayList<>();
        while(head != last){
            int cur = array[head] + array[last];
            int times = 0;
                for (int i = head +1; i < last;i++){

                    if (cur + array[i] == 0){
                        Integer[] in = {array[head],array[last],array[i]};
                        list.add(in);
                        times++;
                    }
                }
            if (cur <= 0){
                head+=1;
                    while(array[head] == array[head-1]){

                        head+=1;
                        if (head >= last-1)
                            break;
                    }

            }else {
                last-=1;
                if(head != last){
                    while(array[last] == array[last-1] && last != head+1){
                        last-=1;
                    }
                }

            }
        }
        System.out.println(list);
    }
}
